added Feb 2001 SDK
[windows-sources.git] / shared source / sscli20 / tools / resourcecompiler / rc.c
blobaaed6bd05e9cd938c1d12c9679eeeae85a2e764c
1 // ==++==
2 //
3 //
4 // Copyright (c) 2006 Microsoft Corporation. All rights reserved.
5 //
6 // The use and distribution terms for this software are contained in the file
7 // named license.txt, which can be found in the root of this distribution.
8 // By using this software in any fashion, you are agreeing to be bound by the
9 // terms of this license.
10 //
11 // You must not remove this notice, or any other, from this software.
12 //
13 //
14 // ==--==
15 // ===========================================================================
16 // File: rc.c
17 // Purpose: Compiles .rc files to .satellite files
18 // ===========================================================================
20 #include "rc.h"
21 #include "palstartup.h"
23 #define BUF_SIZE (4097*2) //Number comes from resource compiler mini spec
24 #define PATH_BUF_SIZE _MAX_PATH
25 #define UNKNOWN_LINE_NUMBER -1
27 #ifdef __GNUC__
28 /* By default, cc treats .rc files as something to be linked in and
29 * therefore won't run the preprocessor on them. The '-x c' option
30 * tells cc to treat the following file as a C file.
31 * On Darwin, we don't want to use the cpp-precomp preprocessor.
33 #if __APPLE_CC__
34 #define COMPILER_PREPROCESSOR_FLAGS "-E -no-cpp-precomp -DRC_INVOKED=1 -DFEATURE_PAL=1 -x c"
35 #else // __APPLE_CC__
36 #define COMPILER_PREPROCESSOR_FLAGS "-E -DRC_INVOKED=1 -DFEATURE_PAL=1 -x c"
37 #endif // __APPLE_CC__
38 #ifdef PLATFORM_UNIX
39 #define COMPILER_NAME "gcc"
40 #else
41 #define COMPILER_NAME "gcc.exe"
42 #endif
43 #define LINEMARKER_PREFIX '#'
44 #define DEFINE_PREFIX "-D"
45 #define INCLUDE_PREFIX "-I"
46 #define TARGET_PREFIX "-o"
47 #else // __GNUC__
48 #define COMPILER_PREPROCESSOR_FLAGS "/nologo /EP /DRC_INVOKED=1 /DFEATURE_PAL=1"
49 #define COMPILER_NAME "cl.exe"
50 #define LINEMARKER_PREFIX "#line"
51 #define DEFINE_PREFIX "/D"
52 #define INCLUDE_PREFIX "/I"
53 #endif // __GNUC__
55 #ifdef PLATFORM_UNIX
56 #define PATH_SEPARATOR_CHAR '/'
57 #define PATH_SEPARATOR_STRING "/"
58 #else
59 #define PATH_SEPARATOR_CHAR '\\'
60 #define PATH_SEPARATOR_STRING "\\"
61 #endif
63 int getEscapeCharacter (char * input, char * output);
64 LPSTR FormatString(LPSTR inputString);
65 LPSTR convertToUTF8(LPSTR inputString);
66 int __cdecl steCompare(const void *first, const void *second);
67 void ProcessParameters(int argc, LPSTR argv[], LPSTR * compilerOptions);
68 void printUsage(LPSTR message);
69 void crashAndBurn(LPSTR message, int lineNumber, LPSTR file, LPSTR badline);
70 void updateLineNumber(LPSTR line, int * lineNumber, LPSTR * lineFile);
71 void preProcessFile(LPSTR compilerOptions);
72 void clearBlanksAndLineUpdates(FILE * file, int * lineNumber,
73 LPSTR * lineFile,
74 LPSTR * textline, BOOL checkCurrent);
75 BOOL startsWithLineMarker(const char *line);
76 BOOL __stdcall ConsoleControlHandler(DWORD dwCtrlType);
77 LPSTR changeExtension(LPSTR filename, LPSTR oldExt, LPSTR newExt);
78 void removeDefinition(FILE * file,
79 LPSTR textline,
80 BOOL linesBeforeBegin,
81 int * lineNumber,
82 LPSTR * lineFile);
83 int LexOneLine(char *p);
84 ULONG Expr_Eval(void);
87 BOOL verbose = FALSE;
88 BOOL warn = FALSE;
89 BOOL nulls = FALSE;
90 int codepage = 1252;
92 LPSTR inFilename;
93 LPSTR outFilename = NULL;
94 LPSTR strippedFilename;
95 FILE * strippedrc;
96 FILE * outfile;
99 //MAIN
101 int __cdecl main(int argc, LPSTR argv[])
103 int lineNumber = 0;
104 LPSTR filenameStart;
105 LPSTR lineFile = NULL;
106 LPSTR textline;
107 LPSTR alloctextline;
108 LPSTR textlinetotok;
109 LPSTR firstword;
111 LPSTR stringID;
112 UINT stringIDint;
113 UINT32 filelenCur;
114 LPSTR stringValue;
115 UINT stringLen;
116 LPSTR stringValueFormatted;
117 LPSTR stringValueOut;
118 STRING_TABLE_ENTRY * stringtable = NULL;
119 int stringTableCounter = 0;
120 char * pchEndQuote;
121 char * pchStartQuote;
123 LPSTR compilerOptions;
125 UINT16 uID;
126 UINT32 stringoffset;
128 int i;
129 int lastValue = -1;
130 BOOL OutFileWritten = FALSE;
131 LONG exprResult;
133 alloctextline = (LPSTR)malloc(BUF_SIZE);
134 if(alloctextline == NULL){
135 crashAndBurn("Out of memory - alloctextline",
136 UNKNOWN_LINE_NUMBER, "", "");
139 SetConsoleCtrlHandler(ConsoleControlHandler, TRUE);
141 argv++;
142 argc--;
143 if (argc == 0){
144 printUsage("");
147 compilerOptions = (LPSTR)malloc(sizeof(" " COMPILER_PREPROCESSOR_FLAGS " "));
149 if (compilerOptions == NULL)
150 crashAndBurn("Out of memory - compilerOptions",
151 UNKNOWN_LINE_NUMBER, "", "");
153 strcpy(compilerOptions, " " COMPILER_PREPROCESSOR_FLAGS " ");
155 ProcessParameters(argc, argv, &compilerOptions);
157 if (inFilename == NULL){
158 printUsage("A filename must be specified\n");
160 else{
161 printf("\n\n%s\n", inFilename);
164 if (verbose){
165 printf("Preprocessing resource file...\n");
168 filenameStart = strrchr(inFilename, PATH_SEPARATOR_CHAR);
169 if (filenameStart == NULL) {
170 filenameStart = inFilename;
171 } else {
172 filenameStart++;
175 lineFile = (LPSTR)malloc(strlen(filenameStart) + 1);
176 if (lineFile == NULL) {
177 crashAndBurn("Out of memory - string allocation",
178 UNKNOWN_LINE_NUMBER, "", "");
180 strcpy(lineFile, filenameStart);
183 // The pre processed file is created in the temp directory
184 char szTempName[MAX_PATH];
185 char szTempPath[MAX_PATH];
187 if (!GetTempPathA(MAX_PATH, szTempPath)) {
188 crashAndBurn("GetTempPath failed!", UNKNOWN_LINE_NUMBER,"","");
191 if (!GetTempFileNameA(szTempPath, "RC", 0, szTempName)) {
192 crashAndBurn("GetTempFileNameA failed!", UNKNOWN_LINE_NUMBER,"","");
195 strippedFilename = (LPSTR)malloc(strlen(szTempName) + 1);
196 if (strippedFilename == NULL) {
197 crashAndBurn("Out of memory - strippedFileName",
198 UNKNOWN_LINE_NUMBER, "", "");
201 strcpy(strippedFilename, szTempName);
204 preProcessFile(compilerOptions);
206 free(compilerOptions);
207 free(inFilename);
209 if (verbose)
210 printf("Reading Preprocessed File %s\n", strippedFilename);
212 //Open stripped input file
213 strippedrc = fopen(strippedFilename, "r");
214 if (strippedrc == NULL){
215 crashAndBurn("Preprocessor failure", UNKNOWN_LINE_NUMBER, "", "");
218 textline=alloctextline;
219 while (fgets(textline, BUF_SIZE, strippedrc) != NULL){
221 lineNumber++;
223 clearBlanksAndLineUpdates(strippedrc, &lineNumber, &lineFile,
224 &textline, TRUE);
225 if (textline == NULL){
226 break;
229 textline[(strlen(textline)-1)] = '\0'; //Remove trailing newline
231 textlinetotok = (LPSTR)malloc(strlen(textline) + 1);
233 if(textlinetotok == NULL){
234 crashAndBurn("Out of memory - textlinetotok",
235 UNKNOWN_LINE_NUMBER, "", "");
238 strcpy(textlinetotok, textline);
240 firstword = strtok(textlinetotok, " \t\n");
241 assert(firstword != NULL); //BLANK LINES WERE REMOVED
243 if (!strcmp(firstword, "STRINGTABLE")){
245 if (verbose)
246 printf("StringTable resource found on line %i in \n\t%s, processing\n",
247 lineNumber, lineFile);
249 clearBlanksAndLineUpdates(strippedrc,&lineNumber,
250 &lineFile, &textline, FALSE);
251 if (textline == NULL){
252 crashAndBurn("Unexpected end of file in Stringtable",
253 lineNumber, lineFile, "");
256 free(textlinetotok);
258 //MAKE SURE BEGIN EXISTS
259 textlinetotok = (LPSTR)malloc(strlen(textline) + 1);
260 if (textlinetotok == NULL)
261 crashAndBurn("Out of memory -textlinetotok",
262 UNKNOWN_LINE_NUMBER,"","");
263 textlinetotok = strcpy(textlinetotok, textline);
265 firstword = strtok(textlinetotok, " \t\n");
266 assert(firstword != NULL); //BLANK LINES WERE REMOVED
268 if ( (strcmp(firstword,"BEGIN")) ){ //NO BEGIN, CHECK FOR {
269 textline = strchr(textline, '{');
270 if (textline == NULL){
271 crashAndBurn("No BEGIN or { following STRINGTABLE",
272 lineNumber, lineFile, textline);
274 textline++;
276 else {
277 textline = alloctextline;
278 if(fgets(textline, BUF_SIZE, strippedrc) == NULL){
279 crashAndBurn("end of file before END in stringtable",
280 lineNumber, lineFile, "");
282 else{
283 lineNumber++;
286 free(textlinetotok);
288 //process entries
289 while(1){
290 clearBlanksAndLineUpdates(strippedrc,
291 &lineNumber, &lineFile, &textline, TRUE);
292 if (textline == NULL){
293 crashAndBurn("end of file before END in stringtable",
294 lineNumber, lineFile, "");
297 textlinetotok = (LPSTR)malloc(strlen(textline) + 1);
298 if (textlinetotok == NULL)
299 crashAndBurn("Out of memory -textlinetotok",
300 UNKNOWN_LINE_NUMBER,"","");
301 textlinetotok = strcpy(textlinetotok, textline);
303 firstword = strtok(textlinetotok, " \t\n");
304 assert(firstword != NULL); //BLANK LINES WERE REMOVED
306 if (!strcmp(firstword,"END")|| !strcmp(firstword, "}")){
307 free(textlinetotok);
308 break;
311 if (stringtable == NULL){ //First time through loop, no memory yet.
312 stringtable = (STRING_TABLE_ENTRY*)malloc(sizeof(STRING_TABLE_ENTRY));
313 if (stringtable == NULL){
314 crashAndBurn("Out of memory - stringtable",
315 UNKNOWN_LINE_NUMBER, "", "");
318 else { //Make room for additional entry
319 stringtable = (STRING_TABLE_ENTRY*)realloc(stringtable,
320 (stringTableCounter + 1) *
321 sizeof(STRING_TABLE_ENTRY));
322 if (stringtable == NULL){
323 crashAndBurn("Out of memory - stringtable",
324 UNKNOWN_LINE_NUMBER, "", "");
328 //GET FRESH COPY OF TEXTLINE TO TOKENIZE WITH DIFFERENT SPACERS
329 free(textlinetotok);
331 textlinetotok = (LPSTR)malloc(strlen(textline) + 1);
332 if (textlinetotok == NULL)
333 crashAndBurn("Out of memory -textlinetotok",
334 UNKNOWN_LINE_NUMBER,"","");
335 textlinetotok = strcpy(textlinetotok, textline) ;
337 stringID = strtok(textlinetotok, "\"");
338 if (stringID == NULL)
339 crashAndBurn("Bad Format in StringTable",
340 lineNumber, lineFile, textline);
342 if (strstr(stringID, "L") != NULL)
343 *(strstr(stringID, "L")) = '\0';
345 if(!LexOneLine(stringID))
346 crashAndBurn("StringID in bad form",
347 lineNumber, lineFile, textline);
349 // Note: we are indexing only using the low 16 bits
350 exprResult = Expr_Eval();
351 if (exprResult == -1) {
352 // Expr_Eval returns -1 if there is no valid number
353 crashAndBurn("No StringID found",
354 lineNumber, lineFile, textline);
356 stringIDint = (UINT16) exprResult;
357 free(textlinetotok);
359 // Cannot use strtok because it will ignore quotes in resources
360 // that appear as "this has a ""quoted"" string"
361 pchStartQuote = strchr(textline, '\"');
362 pchEndQuote = strrchr(textline, '\"');
363 stringValue = NULL;
364 if (pchEndQuote != NULL && pchEndQuote != pchStartQuote)
366 stringLen = pchEndQuote - pchStartQuote + 1;
367 stringValue = (LPSTR)malloc(stringLen + 1);
368 if (stringValue == NULL)
369 crashAndBurn("Out of memory - stringValue",
370 lineNumber, lineFile, textline);
371 stringValue = strncpy(stringValue, pchStartQuote, stringLen);
372 stringValue[stringLen-1] = '\0';
375 if (stringValue == NULL){
376 //STRING VALUE WAS NOT ON SAME LINE AS STRING ID
377 //SEARCH UPCOMING LINES TO SEE IF WE CAN FIND IT
379 int oldLineNumber = lineNumber;
381 clearBlanksAndLineUpdates(strippedrc,
382 &lineNumber, &lineFile, &textline, FALSE);
383 if (textline == NULL){
384 crashAndBurn("Unexpected end of file in stringtable",
385 lineNumber, lineFile, "");
388 if (strchr(textline, '\"') != NULL){
389 // Cannot use strtok because it will ignore quotes in resources
390 // that appear as "this has a ""quoted"" string"
391 pchStartQuote = strchr(textline, '\"');
392 pchEndQuote = strrchr(textline, '\"');
393 stringValue = NULL;
394 if (pchEndQuote != NULL && pchEndQuote != pchStartQuote)
396 stringLen = pchEndQuote - pchStartQuote + 1;
397 stringValue = (LPSTR)malloc(stringLen + 1);
398 if (stringValue == NULL)
399 crashAndBurn("Out of memory - stringValue",
400 oldLineNumber, lineFile, textline);
401 stringValue = strncpy(stringValue, pchStartQuote, stringLen);
402 stringValue[stringLen-1] = '\0';
405 else{
406 /// Found a line which is not blank, but does not have
407 /// a string surrounded by quotes in it
408 crashAndBurn("StringID with no StringValue in STRINGTABLE",
409 oldLineNumber, lineFile, "");
413 stringValueFormatted = FormatString(stringValue + 1); // skip over leading quote
414 if (stringValueFormatted == NULL){
415 crashAndBurn("Unknown escape sequence in String Value",
416 lineNumber,lineFile, textline);
419 stringValueOut = convertToUTF8(stringValueFormatted);
420 free(stringValueFormatted);
421 free(stringValue);
423 stringtable[stringTableCounter].StringID = stringIDint;
424 stringtable[stringTableCounter].StringValue = stringValueOut;
425 stringTableCounter++;
427 // If there is a '}' after the string and new line char, end section
428 if (pchEndQuote) {
429 char * pchNewLine = strchr(pchEndQuote + 1, '\n');
430 char * pchBrace = strchr(pchEndQuote + 1, '}');
432 if (pchNewLine != NULL && pchBrace != NULL && pchNewLine > pchBrace)
433 break;
436 textline = alloctextline;
437 if(fgets(textline, BUF_SIZE, strippedrc) == NULL){
438 crashAndBurn("end of file before END in stringtable",
439 lineNumber, lineFile, "");
441 else{
442 lineNumber++;
449 else if (!strcmp(firstword, "#pragma")){
450 LPSTR pragma;
452 pragma = strtok(NULL, " (");
454 if(!strcmp(pragma, "code_page")){
455 LPSTR cpString;
457 cpString = strtok(NULL, ")");
458 codepage = strtoul(cpString, NULL, 0);
460 if(codepage == 0){ //couldn't conver to int
461 crashAndBurn("Bad Format in code_page pragma",
462 lineNumber, lineFile, textline);
465 if (!IsValidCodePage(codepage)){
466 if (warn)
467 printf("WARNING, codepage %i is invalid\n", codepage);
468 else
469 crashAndBurn("Invalid Code Page Value",
470 lineNumber, lineFile, textline);
472 if(verbose)
473 printf("Codepage pragma found, updating codepage to %i\n", codepage);
475 else if(!strcmp(pragma, "once")){
476 //ignore
478 else if(!strcmp(pragma, "GCC")){
479 //ignore
481 else{
482 crashAndBurn("unknown pragma", lineNumber, lineFile, textline);
484 free(textlinetotok);
486 else if(!strcmp(firstword, "typedef")){
487 int i;
488 ////SKIP ALL LINES UNTIL FIRST {, THEN STOP WHEN FIND } THAT MATCHES
490 printf("WARNING...TYPEDEF in %s:\n %s\n", lineFile, textline);
492 free(textlinetotok);
493 textline = alloctextline;
494 fgets(textline, BUF_SIZE, strippedrc);
495 lineNumber++;
497 while (strstr(textline, "{") == NULL){ //Skipping to first {
499 if (fgets(textline, BUF_SIZE, strippedrc) == NULL){
500 crashAndBurn("Unexpected end of file", ++lineNumber, lineFile, "");
502 else{
503 lineNumber++;
507 textline = strstr(textline, "{");
508 textline++;
510 for (i = 1; i != 0;){
511 //i is the number of { without }
513 lineNumber++;
514 if (strstr(textline, "{") != NULL){
515 textline = strstr(textline, "{");
516 textline++;
517 i++;
519 else if (strstr(textline, "}") != NULL){
520 textline = strstr(textline, "}");
521 textline++;
522 i--;
524 else{
525 textline = alloctextline;
526 if(fgets(textline, BUF_SIZE, strippedrc) == NULL){
527 crashAndBurn("Unexpected end of file", ++lineNumber, lineFile, "");
529 lineNumber++;
530 i = i;
534 else { ///SOME OTHER RESOURCE
535 LPSTR secondword = strtok(NULL, " \t\n");
536 if (secondword == NULL)
537 secondword = "";
539 if(!strcmp(firstword, "FONT")
540 || !strcmp(firstword, "AUTO3STATE")
541 || !strcmp(firstword, "AUTORADIOBUTTON")
542 || !strcmp(firstword, "COMBOBOX")
543 || !strcmp(firstword, "CONTROL")
544 || !strcmp(firstword, "CTEXT")
545 || !strcmp(firstword, "DEFPUSHBUTTON")
546 || !strcmp(firstword, "EDITTEXT")
547 || !strcmp(firstword, "PUSHBOX")
548 || !strcmp(firstword, "PUSHBUTTON")
549 || !strcmp(firstword, "RADIOBUTTON")
550 || !strcmp(firstword, "RTEXT")
551 || !strcmp(firstword, "SCROLLBAR")
552 || !strcmp(firstword, "STATE3")
553 || !strcmp(firstword, "CAPTION")
554 || !strcmp(firstword, "LANGUAGE")
555 || !strcmp(firstword, "MENUITEM")
556 || !strcmp(firstword, "STYLE")
558 if (verbose) //These resources are one line of text in resource file
559 printf("Resource %s found on line % i in \n\t%s, ignoring\n",
560 firstword, lineNumber, lineFile);
561 free(textlinetotok);
562 continue;
565 else if(!strcmp(secondword, "TYPELIB")
566 || !strcmp(secondword, "typelib")
567 || !strcmp(secondword, "ICON")
568 || !strcmp(secondword, "BITMAP")
569 || !strcmp(secondword, "CURSOR")
570 || !strcmp(secondword, "REGISTRY")
571 || !strcmp(secondword, "FONT")
572 || !strcmp(secondword, "MESSAGETABLE")){
573 if (verbose) //These resources are one line of text in resource file
574 printf("Resource %s found on line % i in \n\t%s, ignoring\n",
575 secondword, lineNumber, lineFile);
576 free(textlinetotok);
577 continue;
580 else if (!strcmp(secondword, "VERSIONINFO")
581 || !strcmp(secondword, "TOOLBAR")
582 || !strcmp(secondword, "MENU")
583 || !strcmp(secondword, "ACCELERATORS")
584 || !strcmp(secondword, "DIALOG")
585 || !strcmp(secondword, "TEXTINCLUDE")
586 || !strcmp(secondword, "DIALOGEX")
587 || !strcmp(secondword, "MENUEX")
588 || !strcmp(secondword, "RCDATA")
589 || !strcmp(firstword, "POPUP")){
591 // THESE RESOURCES TAKE UP MORE THEN ONE LINE, DELIMITED BY
592 // BEGIN/END TAGS OR BRACES { }
594 // int i;
596 if (!strcmp(firstword, "POPUP")){
597 secondword = firstword;
600 if (verbose){
601 printf("Resource %s found on line % i in \n\t%s, ignoring\n",
602 secondword, lineNumber, lineFile);
605 removeDefinition(strippedrc, textline, TRUE, &lineNumber, &lineFile);
606 free(textlinetotok);
609 else { //USER DEFINED RESOURCE
611 // OF TWO FORMS, EITHER ONE LINE WITH FILENAME SPECIFYING RESOURCE
612 // OR MULTIPLE LINES, WITH RESOURCE IN SCRIPT SURROUNDED BY BEGIN/END
613 // TAGS OR BRACES { }
615 LPSTR thirdword;
618 printf("UNKNOWN resource %s found on line % i in \n\t%s, ignoring\n",
619 secondword, lineNumber, lineFile);
621 thirdword = strtok(NULL, " \t\n");
623 if (thirdword != NULL){ // RESOURCE DEFINED IN OTHER FILE, ONE LINE
624 free(textlinetotok);
625 continue;
627 else { // RESOURCE IN RESOURCE SCRIPT, SKIP DEFINITION
629 textline = alloctextline;
630 fgets(textline, BUF_SIZE, strippedrc);
631 lineNumber++;
633 removeDefinition(strippedrc, textline, FALSE, &lineNumber, &lineFile);
634 free(textlinetotok);
638 textline = alloctextline;
641 free(alloctextline);
643 qsort((void *)stringtable, stringTableCounter, //sort array by id
644 sizeof(STRING_TABLE_ENTRY), steCompare);
646 //Open output file
647 if (verbose){
648 printf("Opening %s for writing\n",outFilename);
650 outfile = fopen(outFilename,"w+b");
651 if (outfile == NULL){
652 crashAndBurn("Couldn't open file for output",UNKNOWN_LINE_NUMBER, "", "");
655 filelenCur = 0;
656 for(i=0; i<stringTableCounter; i++){
658 if (stringtable[i].StringID == lastValue){ //CHECK FOR DOUBLES
659 LPSTR errorMessage;
661 errorMessage = (LPSTR)malloc(sizeof("Duplicate StringID for Values: ") +
662 strlen(stringtable[i-1].StringValue) +
663 sizeof(", ") +
664 strlen(stringtable[i].StringValue) - 1);
665 if (errorMessage == NULL){
666 crashAndBurn("Out of memory",
667 UNKNOWN_LINE_NUMBER, "", "");
670 errorMessage = strcpy(errorMessage, "Duplicate StringID for Values: ");
671 errorMessage = strcat(errorMessage, stringtable[i-1].StringValue);
672 errorMessage = strcat(errorMessage, ", ");
673 errorMessage = strcat(errorMessage, stringtable[i].StringValue);
675 crashAndBurn(errorMessage, UNKNOWN_LINE_NUMBER, "", "");
677 //ERROR MESSAGE SHOWS VALUES WHICH SHARE AN ID
681 //PRINT OUT TO FILE
682 OutFileWritten = TRUE;
683 // Save string length
684 stringtable[i].StringLen = strlen(stringtable[i].StringValue);
686 // Write string data
687 filelenCur += fwrite(stringtable[i].StringValue, 1, stringtable[i].StringLen, outfile);
689 lastValue = stringtable[i].StringID;
691 for(i=0; i<stringTableCounter; i++){
692 // Done with the StringValue, free it up
693 free(stringtable[i].StringValue);
696 // If odd # of bytes written, pad with an extra to make the table entries
697 // WORD aligned.
698 if (filelenCur & 1)
700 char ch = 0;
702 fwrite(&ch, 1, sizeof(ch), outfile);
705 // Write out the table
706 filelenCur = 0;
707 for(i=0; i<stringTableCounter; i++){
708 uID = VAL16(stringtable[i].StringID);
709 stringoffset = VAL32(filelenCur);
710 filelenCur += stringtable[i].StringLen;
711 // Resource ID
712 fwrite(&uID, 1, sizeof(uID), outfile);
713 // File offset
714 fwrite(&stringoffset, 1, sizeof(stringoffset), outfile);
716 uID = VAL16(-1);
717 stringoffset = VAL32(filelenCur);
718 // Resource ID
719 fwrite(&uID, 1, sizeof(uID), outfile);
720 // File offset
721 fwrite(&stringoffset, 1, sizeof(stringoffset), outfile);
723 if (verbose){
724 printf("Output file is: %s\n\n", outFilename);
727 // CLOSE BOTH OUTPUT FILE AND PREPROCESSOR (.i) FILE
729 if (strippedrc != NULL){
730 fclose(strippedrc);
732 if (outfile != NULL){
733 fclose(outfile);
735 if (!OutFileWritten) {
736 if (verbose) {
737 printf("Removing output file - no STRINGTABLEs were found\n");
739 DeleteFileA(outFilename);
742 //REMOVE .i FILE
744 if(DeleteFileA(strippedFilename)){
745 if(verbose){
746 printf("Removing %s\n", strippedFilename);
750 if (lineFile != NULL) {
751 free(lineFile);
754 if (stringtable) {
755 free(stringtable);
758 return 0;
761 ///////////////////////////////////////////////////////////////////////////////
763 // ConsoleControlHandler
765 // Parameters:
766 // dwCtrlType: the type of signal to be handled
768 // Returns:
769 // false to indicate the program should exit
771 ///////////////////////////////////////////////////////////////////////////////
772 BOOL
773 __stdcall
774 ConsoleControlHandler(DWORD dwCtrlType)
776 if (strippedrc != NULL){
777 fclose(strippedrc);
779 if (outfile != NULL){
780 fclose(outfile);
782 DeleteFileA(strippedFilename);
783 DeleteFileA(outFilename);
785 return FALSE;
788 ///////////////////////////////////////////////////////////////////////////////
790 // removeDefinition
792 ///////////////////////////////////////////////////////////////////////////////
794 void removeDefinition(FILE * file,
795 LPSTR textline,
796 BOOL linesBeforeBegin,
797 int * lineNumber,
798 LPSTR * lineFile)
800 // LPSTR textline = malloc(BUF_SIZE);
801 int i;
804 if(linesBeforeBegin){
805 ////SKIP ALL LINES UNTIL FIRST BEGIN OR {
806 ////THEN STOP WHEN FIND END or }THAT MATCHES
808 while ((strstr(textline, "BEGIN") == NULL) &&
809 (strstr(textline, "{") == NULL)){
810 if (fgets(textline, BUF_SIZE, strippedrc) == NULL){
811 crashAndBurn("Unexpected end of file",
812 ++(*lineNumber), (*lineFile), "");
814 else {
815 (*lineNumber)++;
819 else if ((strstr(textline, "BEGIN") == NULL) &&
820 (strstr(textline, "{") == NULL)){
821 crashAndBurn("Bad Format in User Defined Resource",
822 *lineNumber, *lineFile, textline);
825 if(strstr(textline, "{") != NULL){ //Brace broke the loop
826 textline = strstr(textline, "{");
827 textline++;
829 else{ //BEGIN broke the loop
830 if(fgets(textline, BUF_SIZE, strippedrc) == NULL){
831 crashAndBurn("Unexpected end of file",
832 ++(*lineNumber), (*lineFile), "");
834 else{
835 (*lineNumber)++;
839 for (i = 1; i != 0;){
840 LPSTR word;
841 LPSTR textlinetotok;
843 textlinetotok = (LPSTR)malloc(strlen(textline) + 1);
844 if (textlinetotok == NULL)
845 crashAndBurn("Out of memory -textlinetotok",
846 UNKNOWN_LINE_NUMBER,"","");
847 textlinetotok = strcpy(textlinetotok, textline);
849 word = strtok(textlinetotok, " \t\n");
850 if (word == NULL)
851 word = "";
853 if (textline[0] == '\0')
854 break;
855 //i is the number of opens not yet closed
856 if (!strcmp(word, "BEGIN")){
857 i++;
858 if(fgets(textline, BUF_SIZE, strippedrc) == NULL){
859 crashAndBurn("Unexpected end of file",
860 ++(*lineNumber), (*lineFile), "");
862 else{
863 (*lineNumber)++;
866 else if (strstr(textline, "{") != NULL){
867 i++;
868 textline = strstr(textline, "{");
869 textline++;
871 else if (!strcmp(word, "END")){
872 i--;
873 if(i != 0){
874 if(fgets(textline, BUF_SIZE, strippedrc) == NULL){
875 crashAndBurn("Unexpected end of file",
876 ++(*lineNumber), (*lineFile), "");
879 else{
880 (*lineNumber)++;
883 else if (strstr(textline, "}") != NULL){
884 i--;
885 textline = strstr(textline, "}");
886 textline++;
888 else{
889 if(fgets(textline, BUF_SIZE, strippedrc) == NULL){
890 crashAndBurn("Unexpected end of file",
891 ++(*lineNumber), (*lineFile), "");
893 else{
894 (*lineNumber)++;
897 free(textlinetotok);
900 ///////////////////////////////////////////////////////////////////////////////
902 // crashAndBurn
904 // Parameters:
905 // message: Message to be displayed to stderr
906 // lineNumber: Line number in .rc file in which error occured, or
907 // UNKNOWN_LINE_NUMBER if not line number specific or line
908 // number unknown.
909 // file: File in which the error occured, if using known line number
910 // badline: The line of text which caused the error, if using known line
911 // number.
913 ///////////////////////////////////////////////////////////////////////////////
915 void crashAndBurn(LPSTR message, int lineNumber, LPSTR file, LPSTR badline){
917 if (strippedrc != NULL){
918 fclose(strippedrc);
920 if (outfile != NULL){
921 fclose(outfile);
923 if (strippedFilename)
925 DeleteFileA(strippedFilename);
927 if (outFilename)
929 DeleteFileA(outFilename);
932 fprintf(stderr, "resourcecompiler: fatal error RC0001 : ");
934 if(lineNumber != UNKNOWN_LINE_NUMBER){
935 fprintf(stderr, "on line %d in %s: \n", lineNumber, file);
936 if (badline != NULL && badline[0] != '\0') {
937 fprintf(stderr, "resourcecompiler: %s\n", badline);
940 fprintf(stderr, "resourcecompiler: %s\n", message);
941 exit(1);
945 ///////////////////////////////////////////////////////////////////////////////
947 // ProcessParameters
949 // Parameters:
950 // argc: Number of arguments to be processed
951 // argv[]: Array of parameters
952 // compilerOptions: Options to be passed to the compiler for preprocessing
954 ///////////////////////////////////////////////////////////////////////////////
956 void ProcessParameters(int argc, LPSTR argv[], LPSTR * compilerOptions)
958 char * p;
960 while(argc){
961 if (argc == 1){
962 if (!strcmp(argv[0], "/?") ||
963 !strcmp(argv[0], "-?") ||
964 !strcmp(argv[0], "/h") ||
965 !strcmp(argv[0], "-h")){
966 printUsage("");
968 #if PLATFORM_UNIX
969 // Allow absolute paths
970 else if (**argv == '-') {
971 #else // PLATFORM_UNIX
972 else if(**argv == '/' || **argv == '-'){
973 #endif // PLATFORM_UNIX
974 //LAST ARGUMENT SHOULD NOT BE OPTION, SHOULD BE FILENAME
975 printUsage("A filename must be specified\n");
978 else{ //One arguement is filename
979 FILE * inFile;
980 FILE * inFile2;
981 LPSTR inFileAppend;
983 inFilename = (LPSTR)malloc(strlen(argv[0]) + 1);
984 if (inFilename == NULL){
985 crashAndBurn("Out of memory - inFilename",
986 UNKNOWN_LINE_NUMBER, "", "");
989 strcpy(inFilename, argv[0]);
991 inFileAppend = (LPSTR)malloc(strlen(argv[0]) + sizeof(".rc"));
992 if (inFileAppend == NULL){
993 crashAndBurn("Out of memory - inFileAppend",
994 UNKNOWN_LINE_NUMBER, "", "");
997 inFileAppend = strcpy(inFileAppend, argv[0]);
998 inFileAppend = strcat(inFileAppend, ".rc");
1000 inFile = fopen(inFilename, "r");
1001 inFile2 = fopen(inFileAppend, "r");
1003 if(inFile == NULL && inFile2 == NULL){
1004 LPSTR errorMessage;
1006 errorMessage = (LPSTR)malloc(sizeof("Cannot Open File: ") +
1007 + strlen(inFilename));
1008 if (errorMessage == NULL)
1009 crashAndBurn("Out of memory - errorMessage",
1010 UNKNOWN_LINE_NUMBER, "", "");
1012 errorMessage = strcpy(errorMessage, "Cannot Open File: ");
1013 errorMessage = strcat(errorMessage, inFilename);
1015 free(inFileAppend);
1016 free(inFilename);
1017 crashAndBurn(errorMessage, UNKNOWN_LINE_NUMBER, "", "");
1019 else{
1020 //success!
1022 if (inFile2 != NULL){
1023 fclose(inFile2);
1024 if (inFile == NULL){
1025 free(inFilename);
1026 inFilename = inFileAppend;
1028 else {
1029 free(inFileAppend);
1033 if (inFile != NULL){
1034 fclose(inFile);
1035 free(inFileAppend);
1038 if (outFilename == NULL){
1039 outFilename = changeExtension(inFilename, ".rc", ".satellite");
1044 else if ( (**argv == '/') || (**argv == '-')){
1045 for(p = *argv + 1; *p != '\0'; p++){
1046 switch(tolower(*p)){
1047 case '/':
1048 case '-':
1049 break;
1050 case '?':
1051 case 'h':
1052 printUsage("");
1053 break;
1054 case 'c':
1055 p++;
1056 if(*p == '\0')
1058 argv++;
1059 argc--;
1060 p = *argv;
1063 codepage = strtoul(p, NULL, 16);
1064 if (!IsValidCodePage(codepage)){
1065 if (warn)
1066 printf("WARNING, codepage %i is invalid\n", codepage);
1067 else
1068 crashAndBurn("Invalid Code Page Value",
1069 UNKNOWN_LINE_NUMBER, "", "");
1072 p += strlen(p) - 1;
1073 break;
1075 case 'd':
1076 p++;
1078 if(*p == '\0')
1080 argv++;
1081 argc--;
1082 p = *argv;
1086 *compilerOptions = (LPSTR)realloc(*compilerOptions,
1087 strlen(*compilerOptions) +
1088 sizeof(DEFINE_PREFIX) +
1089 strlen(p) +
1090 sizeof(" ") - 1);
1091 if (*compilerOptions == NULL){
1092 crashAndBurn("Out of memory - compilerOptions",
1093 UNKNOWN_LINE_NUMBER, "", "");
1095 *compilerOptions = strcat(*compilerOptions, DEFINE_PREFIX);
1096 *compilerOptions = strcat(*compilerOptions, p);
1097 *compilerOptions = strcat(*compilerOptions, " ");
1099 p += strlen(p) - 1;
1100 break;
1102 case 'f':
1103 if (*(++p) != 'o'){
1104 printUsage("'f' must be followed by 'o' for renaming output\n");
1106 else{
1107 p++;
1108 outFilename = p;
1109 if(!strcmp(outFilename,""))
1111 argv++;
1112 argc--;
1113 p = *argv;
1116 outFilename = p;
1118 p += strlen(outFilename) - 1;
1119 if (verbose)
1120 printf("Set output file name to %s\n", outFilename);
1121 break;
1123 case 'i':
1124 p++;
1126 if(*p == '\0')
1128 argv++;
1129 argc--;
1130 p = *argv;
1133 *compilerOptions = (LPSTR)realloc(*compilerOptions,
1134 strlen(*compilerOptions) +
1135 sizeof(INCLUDE_PREFIX) +
1136 strlen(p) +
1137 strlen(" "));
1138 if (*compilerOptions == NULL){
1139 crashAndBurn("Out of memory - compilerOptions",
1140 UNKNOWN_LINE_NUMBER, "", "");
1142 *compilerOptions = strcat(*compilerOptions, INCLUDE_PREFIX);
1143 *compilerOptions = strcat(*compilerOptions, p);
1144 *compilerOptions = strcat(*compilerOptions, " ");
1146 p += strlen(p) - 1;
1147 break;
1149 case 'l':
1150 p++;
1152 if(*p == '\0')
1154 argv++;
1155 argc--;
1156 p = *argv;
1159 //THIS ARGUMENT IS NOT USED BY RC!!
1160 //Codepage is set by #pragma(codepage) or \c
1162 p += strlen(p) - 1;
1163 break;
1164 case 'n':
1165 break;
1166 case 'r':
1167 break;
1168 case 'u':
1169 p++;
1171 if(*p == '\0'){
1172 argv++;
1173 argc--;
1174 p = *argv;
1177 *compilerOptions = (LPSTR)realloc(*compilerOptions,
1178 strlen(*compilerOptions) +
1179 sizeof("/U") +
1180 strlen(p) +
1181 strlen(" "));
1182 if (*compilerOptions == NULL){
1183 crashAndBurn("Out of memory - compilerOptions",
1184 UNKNOWN_LINE_NUMBER, "", "");
1186 *compilerOptions = strcat(*compilerOptions, "/U");
1187 *compilerOptions = strcat(*compilerOptions, p);
1188 *compilerOptions = strcat(*compilerOptions, " ");
1190 p += strlen(p) - 1;
1191 break;
1192 case 'v':
1193 verbose = TRUE;
1194 printf("Processing Arguments...\n");
1195 break;
1196 case 'w':
1197 warn = TRUE;
1198 break;
1199 case 'x':
1200 *compilerOptions = (LPSTR)realloc(*compilerOptions,
1201 strlen(*compilerOptions) +
1202 sizeof("/X "));
1203 if (*compilerOptions == NULL){
1204 crashAndBurn("Out of memory - compilerOptions",
1205 UNKNOWN_LINE_NUMBER, "", "");
1207 *compilerOptions = strcat(*compilerOptions, "/X ");
1209 break;
1210 case 'z':
1211 //ARGUMENT IGNORED, PROVIDED FOR COMPATIBILITY
1213 p++;
1215 if(*p == '\0')
1217 argv++;
1218 argc--;
1219 p = *argv;
1222 p += strlen(p) - 1;
1223 break;
1224 default:
1225 printUsage("Unknown argument\n");
1226 break;
1230 else{ //ARGUMENT THAT DOESN'T START WITH / OR - BUT IS NOT LAST
1231 printUsage("Unknown argument, no - or /\n");
1233 argc--;
1234 argv++;
1237 ///////////////////////////////////////////////////////////////////////////////
1239 // printUsage
1241 // Prints the Usage Statement for rc
1243 // Parameters:
1244 // message: A message to be printed before the usage statemnt, often
1245 // indicating what caused the usage statement to be printed
1247 ///////////////////////////////////////////////////////////////////////////////
1249 void printUsage(LPSTR message){
1250 printf(message);
1252 printf("Usage: resourcecompiler [options] inputfile\n");
1253 printf("\nOptions: \n\n");
1254 printf("\t/? Displays a list of resource compiler command-line options.\n");
1255 printf("\t/d Defines a symbol for the preprocessor that you can test\n");
1256 printf("\t with the ifdef directive\n");
1257 printf("\t/fo Rename output file\n");
1258 printf("\t/h Displays a list of command-line options\n");
1259 printf("\t/i Searches the specified directory before searching the\n");
1260 printf("\t directories specified by the INCLUDE enviornment variable\n");
1261 printf("\t/c specifies the codepage to be used for compilation\n");
1262 printf("\t/n Null terminates all strings in the string table\n");
1263 printf("\t/r Ignored. Provided for compatibility with existing\n");
1264 printf("\t makefiles\n");
1265 printf("\t/l Ignored. Provided for compatibility with existing\n");
1266 printf("\t makefiles\n");
1268 printf("\t/z Ignored. Provided for compatibility with existing\n");
1269 printf("\t makefiles\n");
1271 printf("\t/u Undefines a symbol for the preprocessor\n");
1272 printf("\t/v Displays messages that report on the progress of the\n");
1273 printf("\t compiler\n");
1274 printf("\t/w Warns if invalid codepage (default is error)\n");
1275 printf("\t/x Prevents RC from checking INCLUDE enviornment variable\n");
1276 printf("\t when searching for header files or resource files\n\n");
1278 exit(1);
1281 ///////////////////////////////////////////////////////////////////////////////
1283 // preProcessFile
1285 // Calls the C preprocessor on the input file to produce the stripped file.
1287 // Parameters:
1288 // compilerOptions: Command Line arguments to be passed to the compiler
1290 ///////////////////////////////////////////////////////////////////////////////
1291 void preProcessFile(LPSTR compilerOptions)
1293 LPSTR commandLine;
1294 PROCESS_INFORMATION procInfo;
1295 STARTUPINFOA startInfo;
1296 DWORD exitCode;
1297 HANDLE hPreFile;
1298 SECURITY_ATTRIBUTES secAttr;
1300 commandLine = (LPSTR)malloc(strlen(COMPILER_NAME) +
1301 strlen(compilerOptions) +
1302 strlen(inFilename) +
1303 strlen("\"\"") + 1);
1304 if (commandLine == NULL){
1305 crashAndBurn("Out of memory - commandLine", UNKNOWN_LINE_NUMBER, "", "");
1308 commandLine = strcpy(commandLine, COMPILER_NAME);
1310 strcat(commandLine, compilerOptions);
1311 strcat(commandLine, "\"");
1312 strcat(commandLine, inFilename);
1313 strcat(commandLine, "\"");
1315 memset(&startInfo, 0, sizeof(startInfo));
1316 startInfo.cb = sizeof(startInfo);
1318 memset(&secAttr, 0, sizeof(secAttr));
1319 secAttr.nLength = sizeof(secAttr);
1320 secAttr.bInheritHandle = TRUE;
1322 if ((hPreFile = CreateFileA(strippedFilename, GENERIC_WRITE, 0, &secAttr,
1323 CREATE_ALWAYS, 0, NULL)) == INVALID_HANDLE_VALUE) {
1324 crashAndBurn("Failed to create temporary file for preprocessor",
1325 UNKNOWN_LINE_NUMBER,"","");
1328 startInfo.dwFlags = STARTF_USESTDHANDLES;
1329 startInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
1330 startInfo.hStdOutput = hPreFile;
1331 startInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
1333 if (verbose){
1334 printf("\n\n\nC compiler command line is: %s\n\n\n", commandLine);
1337 if(!CreateProcessA(NULL,
1338 commandLine,
1339 NULL,
1340 NULL,
1341 TRUE,
1343 NULL,
1344 NULL,
1345 &startInfo,
1346 &procInfo)){
1347 crashAndBurn("Failed to spawn preprocessor", UNKNOWN_LINE_NUMBER,"","");
1350 WaitForSingleObject( procInfo.hProcess, INFINITE );
1352 GetExitCodeProcess(procInfo.hProcess, &exitCode);
1354 if (exitCode != 0){
1355 crashAndBurn("Preprocessor Failed", UNKNOWN_LINE_NUMBER, "", "");
1358 CloseHandle(procInfo.hProcess);
1359 CloseHandle(procInfo.hThread);
1360 CloseHandle(hPreFile);
1361 free(commandLine);
1365 ///////////////////////////////////////////////////////////////////////////////
1367 // convertToUTF8
1369 // Parameters:
1370 // inputString: pointer to string of characters to be converted
1372 // Returns:
1373 // Pointer to a string converted into UTF8
1375 ///////////////////////////////////////////////////////////////////////////////
1377 LPSTR convertToUTF8(LPSTR inputString)
1380 int i;
1381 int error;
1382 int numOfWideChars;
1383 int numOfOutChars;
1384 LPWSTR wideString;
1385 LPSTR outputString;
1387 numOfWideChars = MultiByteToWideChar(codepage, 0,
1388 inputString,
1389 strlen(inputString),
1390 NULL, 0);
1392 wideString = (LPWSTR)malloc(numOfWideChars*sizeof(WCHAR));
1393 if (wideString == NULL){
1394 crashAndBurn("Out of memory - wideString", UNKNOWN_LINE_NUMBER, "", "");
1397 i = MultiByteToWideChar(codepage,0,inputString,
1398 strlen(inputString),wideString,
1399 numOfWideChars);
1401 if (i == 0){
1402 error = GetLastError();
1403 if (error == ERROR_INSUFFICIENT_BUFFER)
1404 crashAndBurn("Converting to UTF8 - INSUFFICIENT_BUFFER\n",
1405 UNKNOWN_LINE_NUMBER, "", "");
1406 else if (error == ERROR_INVALID_FLAGS)
1407 crashAndBurn("Converting to UTF8 - INVALID_FLAGS\n",
1408 UNKNOWN_LINE_NUMBER, "", "");
1409 else if (error == ERROR_INVALID_PARAMETER)
1410 crashAndBurn("Converting to UTF8 - INVALID_PARAMETER 1\n",
1411 UNKNOWN_LINE_NUMBER, "", "");
1412 else if (error == ERROR_NO_UNICODE_TRANSLATION )
1413 crashAndBurn("Converting to UTF8 - NO_UNICODE_TRANSLATION\n",
1414 UNKNOWN_LINE_NUMBER, "", "");
1415 else
1416 crashAndBurn("Error converting to UTF8\n",
1417 UNKNOWN_LINE_NUMBER, "", "");
1421 numOfOutChars = WideCharToMultiByte(CP_UTF8, 0, wideString,
1422 numOfWideChars, NULL, 0, NULL, NULL);
1425 outputString = (LPSTR)malloc(numOfOutChars + 1);
1426 if (outputString == NULL){
1427 crashAndBurn("Out of memory - outputString", UNKNOWN_LINE_NUMBER, "", "");
1431 i = WideCharToMultiByte(CP_UTF8, 0, wideString,
1432 numOfWideChars, outputString,
1433 numOfOutChars, NULL, NULL);
1435 if (i == 0){
1436 error = GetLastError();
1437 if (error == ERROR_INSUFFICIENT_BUFFER)
1438 crashAndBurn("Converting to UTF8 - INSUFFICIENT_BUFFER\n",
1439 UNKNOWN_LINE_NUMBER, "", "");
1440 else if (error == ERROR_INVALID_FLAGS)
1441 crashAndBurn("Converting to UTF8 - INVALID_FLAGS\n",
1442 UNKNOWN_LINE_NUMBER, "", "");
1443 else if (error == ERROR_INVALID_PARAMETER)
1444 crashAndBurn("Converting to UTF8 - INVALID_PARAMETER 1\n",
1445 UNKNOWN_LINE_NUMBER, "", "");
1446 else
1447 crashAndBurn("Error converting to UTF8\n",
1448 UNKNOWN_LINE_NUMBER, "", "");
1451 outputString[numOfOutChars] = '\0';
1453 free(wideString);
1454 return outputString;
1457 ///////////////////////////////////////////////////////////////////////////////
1459 // FormatString
1461 // Parameters:
1462 // inputString: pointer to string of characters to be formatted
1464 // Returns:
1465 // all character sequences interpretable as escape characters are
1466 // replaced by the corresponding character
1468 ///////////////////////////////////////////////////////////////////////////////
1470 LPSTR FormatString(LPSTR inputString)
1472 LPSTR formattedString;
1473 int i,j, stringLength;
1474 int numOfCharsUsed;
1475 char escchar;
1477 // Gets longer only when % is present
1478 formattedString = (LPSTR)malloc(strlen(inputString) + 1);
1480 if (formattedString == NULL){
1481 crashAndBurn("Out of memory - formattedString",
1482 UNKNOWN_LINE_NUMBER, "", "");
1485 stringLength = (int)strlen(inputString);
1487 for (i = 0, j = 0; i < stringLength; i++, j++){
1489 if ((int) inputString[i] == '\\'){
1490 //replace with escape character
1491 numOfCharsUsed = getEscapeCharacter(&inputString[i+1], &escchar);
1492 if (numOfCharsUsed == 0){
1493 free(formattedString);
1494 return NULL;
1496 i += numOfCharsUsed;
1497 inputString[i] = escchar;
1500 if (inputString[i] == '\"' && inputString[i+1] == '\"') {
1501 // replace "" with "
1502 formattedString[j] = inputString[i];
1503 i++;
1505 else {
1506 //use character given
1507 formattedString[j] = inputString[i];
1510 formattedString[j] = '\0';
1512 return formattedString;
1515 ///////////////////////////////////////////////////////////////////////////////
1517 // changeExtension
1519 // Changes the extension from old to new, or appends new filename didn't
1520 // have extension old
1522 // Parameters:
1523 // filename: filename on which to change extension
1524 // oldExt: old extension
1525 // newExt: new extension
1527 // Returns:
1528 // Pointer to a string containing the new filename
1530 ///////////////////////////////////////////////////////////////////////////////
1531 LPSTR changeExtension(LPSTR filename, LPSTR oldExt, LPSTR newExt)
1534 LPSTR returnValue;
1535 LPSTR tempString;
1537 returnValue = (LPSTR)malloc(strlen(filename) + strlen(newExt));
1538 if (returnValue == NULL)
1539 crashAndBurn("Out of memory - returnValue", UNKNOWN_LINE_NUMBER, "", "");
1541 returnValue = strcpy(returnValue, filename);
1543 tempString = strstr(returnValue, oldExt);
1544 if (tempString != NULL)
1545 tempString[0] = '\0';
1546 returnValue = strcat(returnValue, newExt);
1548 return(returnValue);
1553 ///////////////////////////////////////////////////////////////////////////////
1555 // steCompare
1556 // (for use with qsort)
1558 // Parameters:
1559 // first: pointer to first STRING_TABLE_ENTRY to be compared
1560 // second: pointer to second STRING_TABLE_ENTRY to be compared
1562 // Returns:
1563 // negative value if first->StringID < second->StringID
1564 // zero if first.StringID = second.StringID
1565 // positive value if first->StringID > second->StringID
1567 ///////////////////////////////////////////////////////////////////////////////
1569 int __cdecl steCompare(const void *first, const void *second){
1571 int result = 0;
1573 if (((STRING_TABLE_ENTRY * )first)->StringID >
1574 ((STRING_TABLE_ENTRY * )second)->StringID)
1575 result = 1;
1576 else if (((STRING_TABLE_ENTRY * )first)->StringID ==
1577 ((STRING_TABLE_ENTRY * )second)->StringID)
1578 result = 0;
1579 else
1580 result = -1;
1582 return result;
1585 ///////////////////////////////////////////////////////////////////////////////
1587 // updateLineNumber
1589 // Parameters:
1590 // line: line of the format #line linenumber filename. To be used to
1591 // determine new values for lineNumber and lineFile.
1592 // lineNumber: pointer to the int which holds current line number
1593 // lineFile: pointer to the LPSTR which holds the current file
1594 // corresponding to lineNumber
1596 ///////////////////////////////////////////////////////////////////////////////
1598 void updateLineNumber(LPSTR line, int * lineNumber, LPSTR * lineFile){
1600 LPSTR lineNumberString;
1601 LPSTR linetotok;
1602 LPSTR fileToken;
1603 LPSTR fileTokenCopy;
1605 linetotok = (LPSTR)malloc(strlen(line) + 1);
1606 if (linetotok == NULL)
1607 crashAndBurn("Out of memory - linetotok", UNKNOWN_LINE_NUMBER, "", "");
1608 strcpy(linetotok, line);
1610 strtok(linetotok, " "); ///removes #line
1612 lineNumberString = strtok(NULL, "\"");
1613 *lineNumber = strtoul(lineNumberString, NULL, 0) - 1;
1615 fileToken = strtok(NULL, "\"");
1616 fileTokenCopy = (LPSTR)malloc (strlen(fileToken) + 1);
1617 if (fileTokenCopy == NULL)
1618 crashAndBurn("Out of memory - fileTokenCopy", UNKNOWN_LINE_NUMBER, "", "");
1619 strcpy(fileTokenCopy, fileToken);
1621 if (*lineFile != NULL) {
1622 free(*lineFile);
1624 *lineFile = FormatString(fileTokenCopy);
1625 free(fileTokenCopy);
1627 if (*lineFile == NULL){
1628 printf(line);
1629 crashAndBurn("Error in format of preprocessed file",
1630 UNKNOWN_LINE_NUMBER, "", "");
1633 if (*lineFile == NULL){
1634 crashAndBurn("Out of memory - lineFile", UNKNOWN_LINE_NUMBER, "", "");
1637 free(linetotok);
1639 ///////////////////////////////////////////////////////////////////////////////
1641 // clearBlanksAndLineUpdates
1643 // Parameters:
1644 // file: pointer to file from which the lines are to be taken
1645 // lineNumber: pointer to the int which holds current line number
1646 // lineFile: pointer to the LPSTR which holds the current file
1647 // corresponding to lineNumber
1648 // textline: buffer for lines read from file. if checkCurrent is true,
1649 // will be read as first line to be examined, at return,
1650 // will hold the value of the first non blank or
1651 // line updating line.
1652 // checkCurrent: if true, the value of textline when the function is called
1653 // will be checked for blankness or line update. otherwise the
1654 // initial string in textline will be ignored
1656 ///////////////////////////////////////////////////////////////////////////////
1657 void clearBlanksAndLineUpdates(FILE * file,
1658 int * lineNumber,
1659 LPSTR * lineFile,
1660 LPSTR * textline,
1661 BOOL checkCurrent)
1663 //LOOP TO REMOVE EFFECTIVELY BLANK LINES, AND #line LINES
1666 //IF checkCurrent WAS SET TO TRUE, DON'T READ LINE FIRST TIME
1668 if(!checkCurrent){
1669 if(fgets(*textline, BUF_SIZE, file) == NULL){
1670 *textline = NULL;
1671 break;
1673 else{
1674 (*lineNumber)++;
1677 else{
1678 checkCurrent = FALSE;
1681 // Only recognize the prefix at the start of the line
1682 if(startsWithLineMarker(*textline))
1683 updateLineNumber(*textline, lineNumber, lineFile);
1685 }while((strspn(*textline, " \t\n") == strlen(*textline))
1686 || startsWithLineMarker(*textline));
1690 ///////////////////////////////////////////////////////////////////////////////
1692 // startsWithLineMarker
1694 // Parameters:
1695 // line: a line in a preprocessed file
1697 // Returns:
1698 // Whether the line starts with a preprocessor line directive
1700 ///////////////////////////////////////////////////////////////////////////////
1703 BOOL startsWithLineMarker(const char *line)
1705 #ifdef __GNUC__
1706 return (line[0] == LINEMARKER_PREFIX && (line[1] == ' ' || isdigit(line[1])));
1707 #else
1708 return (strstr(line, (const char*)LINEMARKER_PREFIX) == line);
1709 #endif
1712 ///////////////////////////////////////////////////////////////////////////////
1714 // getEscapeCharacter
1716 // Parameters:
1717 // input: pointer to string of characters to be interpreted as escape
1718 // sequence
1719 // output: pointer to location in which to store escape character
1721 // Returns:
1722 // Number of characters used to form escape sequence if successfull,
1723 // 0 if string could not be interpreted
1725 ///////////////////////////////////////////////////////////////////////////////
1728 int getEscapeCharacter (char * input, char * output){
1730 int i;
1731 int value;
1732 int c;
1734 switch(*input){
1735 case 'x':
1736 for (i = 0, value = 0; isxdigit(input[i]); i++){
1737 c = input[i];
1738 if (isupper(c)){
1739 c -= ('A' - 10);
1741 else if (islower(c)){
1742 c -= ('a' - 10);
1744 else{
1745 c -= '0';
1747 value = (16 * value) + c;
1749 if(!i){
1750 fprintf(stderr, "Bad Hex value while converting escape character\n");
1751 return 0;
1753 else if (value < 255){
1754 fprintf(stderr,
1755 "Out of range Hex value while converting escape character\n");
1756 return 0;
1759 *output = (char) value;
1760 return i + 1;
1762 case '0':
1763 case '2':
1764 case '3':
1765 case '4':
1766 case '5':
1767 case '6':
1768 case '7':
1769 for ( i = 0, value = 0;
1770 input[i] <= '7' && input[i] >= '0' && i <= 3;
1771 i++){
1772 value = (8 * value) + (input[i] - '0');
1774 if (!i){
1775 printf("Bad Octal value while converting escape character\n");
1776 return 0;
1778 else if (value > 255){
1779 printf("Out of range Octal value while converting escape character\n");
1780 return 0;
1783 *output = (char) value;
1784 return i + 1;
1786 case 'a':
1787 *output = '\a';
1788 return 1;
1789 case 'b':
1790 *output = '\b';
1791 return 1;
1792 case 'f':
1793 *output = '\f';
1794 return 1;
1795 case 'n':
1796 *output = '\n';
1797 return 1;
1798 case 'r':
1799 *output = '\r';
1800 return 1;
1801 case 't':
1802 *output = '\t';
1803 return 1;
1804 case 'v':
1805 *output = '\v';
1806 return 1;
1807 case '?':
1808 case '\\':
1809 case '\'':
1810 *output = *input;
1811 return 1;
1813 default:
1814 return 0;